//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
/* Copyright 1999-2001 Intel Corp.  */
#include <elatypes.h>
#include "xllp_keypad.h"
#include "xllp_gpio.h"

extern int CDECL cprintf (const char *, ...);
extern void D_Delay(unsigned int uSec);

XLLP_UINT32_T KP_Status;//BUGBUG: ReadOnly, must be init
//#define NMD

//---------------------------------------------------------------------------------------------------------------
// Function: ReadScanCodeAutomatically
// Purpose:  This functions reads the scan code from the KeyPad controller triggered by setting of the ASACT bit
//           in the KeyPad Control register. If there is a valid key detected then it returns the scan code.
// Returns:  success/failure.
//---------------------------------------------------------------------------------------------------------------

XLLP_BOOL_T ReadScanCodeAutomatically(XLLP_KEYPAD_REGS *v_pKeyPadRegs,XLLP_UINT8_T *key)
{
    XLLP_BOOL_T     retval=XLLP_FALSE;
    XLLP_UINT32_T   C = 0, R = 0, RthBit = 0,c0,c1,c2,c3,c4,c5,c6,c7;
    XLLP_UINT32_T   numOfKeysPressed=0;

    //v_pKeyPadRegs->kpControlReg |= 1 << 30;

    KP_Status = v_pKeyPadRegs->kpControlReg;

//    cprintf("Keypad status %x, scan %x\n", KP_Status, v_pKeyPadRegs->kpAutomaticScanReg);

    if(KP_Status & MATRIX_INTR_BIT)
    {
        numOfKeysPressed = ((v_pKeyPadRegs->kpAutomaticScanReg & MULTI_KEYS_PRESS) >> 26);
        //cprintf("XLLP:ReadScanCodeAutomatically numOfKeysPressed %x>\r\n", numOfKeysPressed);

// checks to see if it was a "Major" key that was pressed
        if(numOfKeysPressed == 1)
        {
            C =  (v_pKeyPadRegs->kpAutomaticScanReg & COL_SELECTED_MASK);
            R =  (v_pKeyPadRegs->kpAutomaticScanReg & ROW_SELECTED_MASK);
            *key = (unsigned char) (C | R);
        }
        else
// if it was a "Minor" key, then more than one key was activated and that is how
//  you can determine which register to read from.
        if(numOfKeysPressed > 1)
        {
            c0 = v_pKeyPadRegs->kpAutoScanMultiKeyPress0 & 0xFF;
            c1 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress0 >> 16) & 0xFF);
            c2 = v_pKeyPadRegs->kpAutoScanMultiKeyPress1 & 0xFF;
            c3 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress1 >> 16) & 0xFF);
            c4 = v_pKeyPadRegs->kpAutoScanMultiKeyPress2 & 0xFF;
            c5 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress2 >> 16) & 0xFF);
            c6 = v_pKeyPadRegs->kpAutoScanMultiKeyPress3 & 0xFF;
            c7 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress3 >> 16) & 0xFF);

// these keys are the "minor keys", the ones that needs top right and bottom left of the
// cooresponding 4 keys surrounding them to trigger the correct key. Doing a binary search
// there are 5 keys, the middle key reads 0x8, the first key reads 0x2 and the last reads 0x20.
// this needs to be done for each row.  This will be encorporated into a routine for the next
// upgrade of keypad.
            if(c0!=0)
            {
                C = 0x7;
                if(c0 == 0x8)
                {
                   RthBit = 0x2;
                }
                else
                if(c0 > 0x8)
                {
                    if (c0 < 0x20)
                       RthBit = 0x3;
                    else
                       RthBit = 0x4;
                }else
                {
                    if (c0 > 0x2)
                       RthBit = 0x1;
                    else
                       RthBit = 0x0;
                }
            }else
            if(c1!=0)
            {
                C = 0x8;
                if(c1 == 0x8)
                {
                   RthBit = 0x2;
                }
                else
                if(c1 > 0x8)
                {
                    if (c1 < 0x20)
                       RthBit = 0x3;
                    else
                       RthBit = 0x4;
                }else
                {
                    if (c1 > 0x2)
                       RthBit = 0x1;
                    else
                       RthBit = 0x0;
                }
            }else
            if(c2!=0)
            {
                C = 0x9;
                if(c2 == 0x8)
                {
                   RthBit = 0x2;
                }
                else
                if(c2 > 0x8)
                {
                    if (c2 < 0x20)
                       RthBit = 0x3;
                    else
                       RthBit = 0x4;
                }else
                {
                    if (c2 > 0x2)
                       RthBit = 0x1;
                    else
                       RthBit = 0x0;
                }
            }else
            if(c3!=0)
            {
                C = 0xa;
                if(c3 == 0x8)
                {
                   RthBit = 0x2;
                }
                else
                if(c3 > 0x8)
                {
                    if (c3 < 0x20)
                       RthBit = 0x3;
                    else
                       RthBit = 0x4;
                }else
                {
                    if (c3 > 0x2)
                       RthBit = 0x1;
                    else
                       RthBit = 0x0;
                }
            }else
            if(c4!=0)
            {
                C = 0xb;
                if(c4 == 0x8)
                {
                   RthBit = 0x2;
                }
                else
                if(c4 > 0x8)
                {
                    if (c4 < 0x20)
                       RthBit = 0x3;
                    else
                       RthBit = 0x4;
                }else
                {
                    if (c4 > 0x2)
                       RthBit = 0x1;
                    else
                       RthBit = 0x0;
                }
            }
            *key = (unsigned char) ((C<<4) | RthBit);
        }
        else
            *key = NO_KEY;

//       RETAILMSG(1,(TEXT("R is %x C is %x key is %x\r\n"), RthBit,C,*key));

        retval = XLLP_TRUE;
    }
//    RETAILMSG(1,(TEXT("ReadScanCodeAutomatically<\r\n")));
    return(retval);
}

//---------------------------------------------------------------------------------------------------------------
// Function: ReadDirectKeys
// Purpose:  This function looks for any Thumbwheel movement or button press and returns a scan code.
// Returns:  success/failure.
//---------------------------------------------------------------------------------------------------------------

XLLP_BOOL_T ReadDirectKeys(XLLP_KEYPAD_REGS *v_pKeyPadRegs,XLLP_UINT8_T *key)
{
    XLLP_UINT32_T CurrCount,SaveKpRotaryEncoderCountReg;
    static XLLP_UINT32_T PrevCount=START_VALUE;
    XLLP_BOOL_T retval;
    if(KP_Status & DIRECT_INTR_BIT)
    {
        cprintf("ERR: Direct Keys\n");
        SaveKpRotaryEncoderCountReg = v_pKeyPadRegs->kpRotaryEncoderCountReg;
        CurrCount = SaveKpRotaryEncoderCountReg & COUNT_MASK;
        if(SaveKpRotaryEncoderCountReg & OVERFLOW_ROTARY_ENC_0)
        {
            v_pKeyPadRegs->kpRotaryEncoderCountReg = START_VALUE;
            PrevCount   = START_VALUE;
            *key    = SCAN_CODE_SCROLL_UP;                  // Scroll Up
        }
        else if(SaveKpRotaryEncoderCountReg & UNDERFLOW_ROTARY_ENC_0)
        {
            v_pKeyPadRegs->kpRotaryEncoderCountReg = START_VALUE;
            PrevCount   = START_VALUE;
            *key    = SCAN_CODE_SCROLL_DOWN;                // Scroll Down
        }
        else if(CurrCount > PrevCount)
        {
            *key    = SCAN_CODE_SCROLL_UP;
            PrevCount   = CurrCount;                        // Scroll Up
        }
        else if(CurrCount < PrevCount)
        {
            *key    = SCAN_CODE_SCROLL_DOWN;
            PrevCount   = CurrCount;                        // Scroll Down
        }
        else if(v_pKeyPadRegs->kpDirectKeyReg & DIRECT_KEY_IN_2)
        {
            *key    = SCAN_CODE_ACTION;                     // Action Key
        }

        retval = XLLP_TRUE;                                 // Signal availability
    }
    else
    {
        retval = XLLP_FALSE;
    }

    return(retval);
}

//---------------------------------------------------------------------------------------------------------------
// Function: XllpReadScanCode
// Purpose:
// Returns:
//---------------------------------------------------------------------------------------------------------------

XLLP_BOOL_T XllpReadScanCode(XLLP_KEYPAD_REGS *v_pKeyPadRegs,XLLP_UINT8_T *pui8Data)
{
    // Initialise to NO Key scan code, same as key UP
    XLLP_UINT8_T key = NO_KEY;

    if(!ReadDirectKeys(v_pKeyPadRegs,&key))
        ReadScanCodeAutomatically(v_pKeyPadRegs,&key);

    // Assign the Key Here
    *pui8Data = key;

    if(*pui8Data == NO_KEY)
        return(XLLP_FALSE);
    else
        return(XLLP_TRUE);      // Enjoy
}

//---------------------------------------------------------------------------------------------------------------
// Function: XllpSetUpKeyPadInterrupts
// Purpose:  This function will Enable/Disable the Interrupts.
// Returns:  success/failure.
//---------------------------------------------------------------------------------------------------------------
XLLP_BOOL_T XllpSetUpKeyPadInterrupts(XLLP_KEYPAD_REGS *v_pKeyPadRegs,XLLP_BOOL_T fEnDis)
{

    if(fEnDis)
    {
        EN_DIRECT_KEYS_INTR();
        EN_MAT_KEYS_INTR();
    }
    else
    {
        DISABLE_DIRECT_KEYS_INTR();
        DISABLE_MAT_KEYS_INTR();
    }
    return(XLLP_TRUE);
}

//-----------------------------------------------------------------------------------------
// Function: XllpKeyPadConfigure
// Purpose:  This function configures the KeyPad Controller on Bulverde-Mainstone with the
//           appropriate settings.
// Returns:  success/failure.
//------------------------------------------------------------------------------------------
//  PIN#      GPIO PIN NAME           DIRECTION         ALTERNATE FUNCTION
//
//   95         KP_DKIN[2]              Input                   3
//   97         KP_DKIN[4]              Input                   3
//   98         KP_DKIN[5]              Input                   3
//   99         KP_DKIN[6]              Input                   3
//  100         KP_MKIN[0]              Input                   1
//  101         KP_MKIN[1]              Input                   1
//  102         KP_MKIN[2]              Input                   1
//
//   96         KP_DKIN[3]              Output                  3
//  103         KP_MKOUT[0]             Output                  2
//  104         KP_MKOUT[1]             Output                  2
//  105         KP_MKOUT[2]             Output                  2
//  106         KP_MKOUT[3]             Output                  2
//  107         KP_MKOUT[4]             Output                  2
//  108         KP_MKOUT[5]             Output                  2
//
//  93          KP_DKIN[0]              Input                   1
//  94          KP_DKIN[1]              Input                   1
//--------------------------------------------------------------------------------------------

XLLP_BOOL_T XllpKeyPadConfigure(XLLP_KEYPAD_REGS *v_pKeyPadRegs,XLLP_GPIO_T *v_pGPIOReg)
{
    XLLP_BOOL_T retval=XLLP_FALSE;
//  XLLP_UINT32_T GpioDirOutList[]={7,96,103,104,105,106,107,108};
//  XLLP_UINT32_T GpioDirInList[]={9,93,94,95,97,98,99,100,101,102};
//  XLLP_UINT32_T GpioAltFnPinList[]={16,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108};
//  XLLP_UINT32_T GpioAltFnValList[]={16, 1, 1, 3, 3, 3, 3, 3,  1,  1,  1,  2,  2,  2,  2,  2,  2};
    XLLP_UINT32_T GpioDirOutList[]={5, 103,104,105,108,96};
    XLLP_UINT32_T GpioDirInList[]={6, 100,101,102,97,98,99};
    XLLP_UINT32_T GpioAltFnPinList[]={11,96,97,98,99,100,101,102,103,104,105,108};
    XLLP_UINT32_T GpioAltFnValList[]={11, 3, 3, 3, 3,  1,  1,  1,  2,  2,  2,  2};
    if(v_pGPIOReg != 0)
    {
        XllpGpioSetDirectionIn(v_pGPIOReg, GpioDirInList);
        XllpGpioSetDirectionOut(v_pGPIOReg, GpioDirOutList);
        XllpGpioSetOutput0(v_pGPIOReg, GpioDirOutList);
        XllpGpioSetAlternateFn(v_pGPIOReg, GpioAltFnPinList, GpioAltFnValList);
    }


    *(volatile unsigned long*)(0x81300004) |= 1 << 19;//temp CKEN

    if(v_pKeyPadRegs != 0)
    {//BUGBUG::
        // Init the control regs for direct keys
        v_pKeyPadRegs->kpControlReg = (MATRIX_KP_NUMBER_OF_ROWS |  MATRIX_KP_NUMBER_OF_COLUMNS |
                                        MAT_SCAN_LINE0 | MAT_SCAN_LINE1 | MAT_SCAN_LINE2 |
                                        //MAT_SCAN_LINE3 | MAT_SCAN_LINE4 |
                                        MAT_SCAN_LINE5 | MAT_SCAN_LINE6 | //MAT_SCAN_LINE7 |
                                        IGNORE_MULTIPLE_KEY_PRESS |
                                        AUTO_SCAN_ON_ACTIVITY | MATRIX_INTR_ENABLE |
                                        MATRIX_KP_ENABLE
                                        // | ROTARY_ENCODER_ZERO_DEB |
                                        //DIRECT_KP_INTR_ENABLE | DIRECT_KEY_NUMS |
                                        //DIRECT_KP_ENABLE | ROTARY_ENCODER_0_ENABLE
                                        ) ;  //NMD

        v_pKeyPadRegs->kpRotaryEncoderCountReg = START_VALUE;
        v_pKeyPadRegs->kpKeyDebounceInterval = 0;
        retval = XLLP_TRUE;
    }

    D_Delay(100*1000);//Dealy 10 mS for Controller and GPIOs to set up
    return(retval);
}

XLLP_KEYPAD_REGS *pKeyPadRegs = (XLLP_KEYPAD_REGS *)0x81500000;
XLLP_GPIO_T *pGPIOReg = (XLLP_GPIO_T *)0x80E00000;

int KeypadInit()
{
    //int count = 1000;

    //XLLP_UINT8_T ui8Data;
    XllpKeyPadConfigure(pKeyPadRegs, pGPIOReg);

    XllpSetUpKeyPadInterrupts(pKeyPadRegs, XLLP_TRUE);

    KP_Status = pKeyPadRegs->kpControlReg;
    cprintf("KP_Status = %x\n", KP_Status);

    return 0;

// keypad test
//    while(count--) {
//        XLLP_BOOL_T bret = XllpReadScanCode(pKeyPadRegs, &ui8Data);
//        //if (bret)
//        {
//            cprintf("Key = %x\n", ui8Data);
//        }
//        EzSleep(1000*10000);
//        chat();
//    }
//    return 0;
}

unsigned char KeypadRead()
{
    XLLP_UINT8_T ui8Data;
    //XLLP_BOOL_T bret =
    XllpReadScanCode(pKeyPadRegs, &ui8Data);
    //if (bret)
    {
        //cprintf("Key = %x\n", ui8Data);
    }
    return ui8Data;
}

unsigned char CheckKeyHold()
{
    XLLP_UINT32_T C = 0, R = 0;
    XLLP_UINT32_T numOfKeysPressed = 0;
    XLLP_UINT8_T  ui8Data = NO_KEY;

    KP_Status = pKeyPadRegs->kpControlReg;

    //cprintf("Keypad status %x, scan %x\n", KP_Status, pKeyPadRegs->kpAutomaticScanReg);

    //if(KP_Status & MATRIX_INTR_BIT)
    {
        numOfKeysPressed = ((pKeyPadRegs->kpAutomaticScanReg & MULTI_KEYS_PRESS) >> 26);
        //cprintf("XLLP:ReadScanCodeAutomatically numOfKeysPressed <%x>\r\n", numOfKeysPressed);

        if(numOfKeysPressed == 1) {
            C =  (pKeyPadRegs->kpAutomaticScanReg & COL_SELECTED_MASK);
            R =  (pKeyPadRegs->kpAutomaticScanReg & ROW_SELECTED_MASK);
            ui8Data = (unsigned char) (C | R);
        }

        //cprintf("R is %x C is %x key is %x\r\n", RthBit , C, ui8Data);

    }
    return (unsigned char)(ui8Data);
}
